package com.kexio.file.service.impl;

import java.io.InputStream;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kexio.common.dto.PageRequest;
import com.kexio.common.dto.PageResponse;
import com.kexio.dynamic.datasource.annotation.Master;
import com.kexio.dynamic.datasource.annotation.Slave;
import com.kexio.dynamic.feature.annotation.FeatureToggle;
// TODO: 企业级功能注解暂时注释，待framework完善后启用
// import com.kexio.enterprise.governance.ratelimit.annotation.RateLimit;
// import com.kexio.enterprise.observability.logging.annotation.OperateLog;
import com.kexio.file.domain.FileInfo;
import com.kexio.file.dto.FileChunk;
import com.kexio.file.dto.FileUploadRequest;
import com.kexio.file.dto.FileUploadResult;
import com.kexio.file.mapper.FileInfoMapper;
import com.kexio.file.service.FileService;
import com.kexio.file.storage.StorageStrategy;

/**
 * 文件服务实现类
 * 
 * @author kexio
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class FileServiceImpl implements FileService {

    private static final Logger log = LoggerFactory.getLogger(FileServiceImpl.class);

    @Autowired
    private FileInfoMapper fileInfoMapper;

    @Autowired
    private List<StorageStrategy> storageStrategies;

    @Value("${kexio.file.defaultStorage:local}")
    private String defaultStorageType;

    @Value("${kexio.file.enableInstantUpload:true}")
    private boolean enableInstantUpload;

    @Value("${kexio.file.maxFileSize:104857600}") // 100MB
    private long maxFileSize;

    // 分片上传临时信息存储
    private final Map<String, FileUploadRequest> chunkUploadInfo = new ConcurrentHashMap<>();

    @Override
    @Master
    // @RateLimit(key = "file_upload", rate = 10, per = 60, description = "文件上传限流：每分钟10次")
    // @OperateLog(module = "文件管理", operation = "文件上传", type = OperateLog.OperationType.CREATE,
    //            detail = "上传文件: #{request.originalFilename}", businessKey = "#{request.originalFilename}")
    public FileUploadResult uploadFile(FileUploadRequest request, InputStream inputStream) {
        log.info("开始上传文件: {}, 大小: {}", request.getOriginalFilename(), request.getFileSize());
        
        // 参数验证
        validateUploadRequest(request);
        
        // 检查是否支持秒传
        if (enableInstantUpload && request.getFileHash() != null) {
            FileInfo existingFile = checkInstantUpload(request.getFileHash(), request.getFileSize());
            if (existingFile != null) {
                return instantUpload(request, existingFile.getId());
            }
        }
        
        // 获取存储策略
        StorageStrategy strategy = getStorageStrategy(defaultStorageType);
        
        // 上传文件到存储
        FileUploadResult uploadResult = strategy.upload(request, inputStream);
        
        // 保存文件信息到数据库
        FileInfo fileInfo = createFileInfo(request, uploadResult);
        fileInfoMapper.insert(fileInfo);
        
        // 设置文件ID
        uploadResult.setFileId(fileInfo.getId());
        
        log.info("文件上传完成: fileId={}, storageKey={}", fileInfo.getId(), uploadResult.getStorageKey());
        return uploadResult;
    }

    @Override
    @Slave
    @Cacheable(value = "instant-upload", key = "#fileHash + ':' + #fileSize", unless = "#result == null")
    public FileInfo checkInstantUpload(String fileHash, Long fileSize) {
        if (!enableInstantUpload || fileHash == null) {
            return null;
        }
        
        LambdaQueryWrapper<FileInfo> query = new LambdaQueryWrapper<>();
        query.eq(FileInfo::getFileHash, fileHash)
             .eq(FileInfo::getFileSize, fileSize)
             .eq(FileInfo::getStatus, 0)
             .eq(FileInfo::getDeleted, 0);
             
        return fileInfoMapper.selectOne(query);
    }

    @Override
    @Master
    // @OperateLog(module = "文件管理", operation = "文件秒传", type = OperateLog.OperationType.CREATE,
    //            detail = "秒传文件: #{request.originalFilename}", businessKey = "#{existingFileId}")
    public FileUploadResult instantUpload(FileUploadRequest request, Long existingFileId) {
        log.info("执行文件秒传: {}, existingFileId={}", request.getOriginalFilename(), existingFileId);
        
        FileInfo existingFile = fileInfoMapper.selectById(existingFileId);
        if (existingFile == null) {
            throw new RuntimeException("原文件不存在");
        }
        
        // 创建新的文件记录（引用相同的存储）
        FileInfo newFileInfo = createFileInfo(request, null);
        newFileInfo.setStorageType(existingFile.getStorageType());
        newFileInfo.setStorageKey(existingFile.getStorageKey());
        newFileInfo.setStoragePath(existingFile.getStoragePath());
        fileInfoMapper.insert(newFileInfo);
        
        // 获取存储策略
        StorageStrategy strategy = getStorageStrategy(existingFile.getStorageType());
        String accessUrl = strategy.getAccessUrl(existingFile.getStorageKey(), 0);
        
        FileUploadResult result = new FileUploadResult();
        result.setFileId(newFileInfo.getId());
        result.setStorageKey(existingFile.getStorageKey());
        result.setAccessUrl(accessUrl);
        result.setFileHash(existingFile.getFileHash());
        result.setFileSize(existingFile.getFileSize());
        result.setInstantUpload(true);
        result.setStorageType(existingFile.getStorageType());
        
        log.info("文件秒传完成: newFileId={}, originalFileId={}", newFileInfo.getId(), existingFileId);
        return result;
    }

    @Override
    @Master
    // @RateLimit(key = "chunk_upload_init", rate = 5, per = 60)
    // @OperateLog(module = "文件管理", operation = "初始化分片上传", type = OperateLog.OperationType.CREATE,
    //            detail = "初始化分片上传: #{request.originalFilename}")
    public String initChunkUpload(FileUploadRequest request) {
        validateUploadRequest(request);
        
        StorageStrategy strategy = getStorageStrategy(defaultStorageType);
        String uploadId = strategy.initMultipartUpload(request);
        
        // 缓存上传信息
        chunkUploadInfo.put(uploadId, request);
        
        log.info("初始化分片上传: uploadId={}, file={}", uploadId, request.getOriginalFilename());
        return uploadId;
    }

    @Override
    @Master
    // @RateLimit(key = "chunk_upload", rate = 50, per = 60)
    public String uploadChunk(String uploadId, FileChunk chunk) {
        FileUploadRequest request = chunkUploadInfo.get(uploadId);
        if (request == null) {
            throw new RuntimeException("分片上传会话不存在: " + uploadId);
        }
        
        StorageStrategy strategy = getStorageStrategy(defaultStorageType);
        String chunkId = strategy.uploadChunk(uploadId, chunk);
        
        log.debug("分片上传完成: uploadId={}, chunkNumber={}/{}", 
                 uploadId, chunk.getChunkNumber(), chunk.getTotalChunks());
        return chunkId;
    }

    @Override
    @Master
    // @OperateLog(module = "文件管理", operation = "合并分片", type = OperateLog.OperationType.UPDATE,
    //            detail = "合并分片: uploadId=#{uploadId}, chunks=#{chunkIds.size()}")
    public FileUploadResult mergeChunks(String uploadId, List<String> chunkIds, FileUploadRequest request) {
        StorageStrategy strategy = getStorageStrategy(defaultStorageType);
        
        // 合并分片
        FileUploadResult uploadResult = strategy.mergeChunks(uploadId, chunkIds);
        
        // 使用传入的request或缓存的request
        FileUploadRequest finalRequest = request != null ? request : chunkUploadInfo.get(uploadId);
        if (finalRequest == null) {
            throw new RuntimeException("分片上传信息缺失: " + uploadId);
        }
        
        // 保存文件信息
        FileInfo fileInfo = createFileInfo(finalRequest, uploadResult);
        fileInfoMapper.insert(fileInfo);
        uploadResult.setFileId(fileInfo.getId());
        
        // 清理临时信息
        chunkUploadInfo.remove(uploadId);
        
        log.info("分片合并完成: fileId={}, uploadId={}", fileInfo.getId(), uploadId);
        return uploadResult;
    }

    @Override
    @Master
    public boolean abortChunkUpload(String uploadId) {
        StorageStrategy strategy = getStorageStrategy(defaultStorageType);
        boolean success = strategy.abortMultipartUpload(uploadId);
        
        // 清理临时信息
        chunkUploadInfo.remove(uploadId);
        
        log.info("取消分片上传: uploadId={}, success={}", uploadId, success);
        return success;
    }

    @Override
    @Slave
    // @RateLimit(key = "file_download", rate = 100, per = 60)
    public InputStream downloadFile(Long fileId, Long userId) {
        FileInfo fileInfo = getFileById(fileId);
        if (fileInfo == null) {
            throw new RuntimeException("文件不存在: " + fileId);
        }
        
        // 检查权限
        if (!checkFilePermission(fileId, userId, "read")) {
            throw new RuntimeException("无文件访问权限");
        }
        
        StorageStrategy strategy = getStorageStrategy(fileInfo.getStorageType());
        InputStream inputStream = strategy.download(fileInfo.getStorageKey());
        
        // 异步更新访问统计
        updateFileAccessStats(fileId, userId);
        
        log.info("文件下载: fileId={}, userId={}", fileId, userId);
        return inputStream;
    }

    @Override
    @Slave
    @Cacheable(value = "file-access-url", key = "#fileId + ':' + #expireSeconds")
    public String getFileAccessUrl(Long fileId, Long userId, int expireSeconds) {
        FileInfo fileInfo = getFileById(fileId);
        if (fileInfo == null) {
            throw new RuntimeException("文件不存在: " + fileId);
        }
        
        // 检查权限
        if (!checkFilePermission(fileId, userId, "read")) {
            throw new RuntimeException("无文件访问权限");
        }
        
        StorageStrategy strategy = getStorageStrategy(fileInfo.getStorageType());
        return strategy.getAccessUrl(fileInfo.getStorageKey(), expireSeconds);
    }

    @Override
    @Master
    @CacheEvict(value = {"files", "file-access-url"}, key = "#fileId")
    // @OperateLog(module = "文件管理", operation = "删除文件", type = OperateLog.OperationType.DELETE,
    //            detail = "删除文件ID: #{fileId}", businessKey = "#{fileId}")
    public boolean deleteFile(Long fileId, Long userId) {
        if (!checkFilePermission(fileId, userId, "delete")) {
            throw new RuntimeException("无文件删除权限");
        }
        
        int result = fileInfoMapper.softDeleteById(fileId);
        log.info("文件删除: fileId={}, userId={}, success={}", fileId, userId, result > 0);
        return result > 0;
    }

    @Override
    @Master
    @FeatureToggle(value = "batch_file_operations", description = "批量文件操作功能",
                   onDisabled = FeatureToggle.DisabledStrategy.THROW_EXCEPTION,
                   disabledMessage = "批量文件操作暂时不可用")
    // @RateLimit(key = "batch_delete_files", rate = 3, per = 300)
    // @OperateLog(module = "文件管理", operation = "批量删除文件", type = OperateLog.OperationType.DELETE,
    //            detail = "批量删除文件，数量: #{fileIds.size()}")
    public int batchDeleteFiles(List<Long> fileIds, Long userId) {
        int successCount = 0;
        for (Long fileId : fileIds) {
            if (checkFilePermission(fileId, userId, "delete")) {
                if (fileInfoMapper.softDeleteById(fileId) > 0) {
                    successCount++;
                }
            }
        }
        log.info("批量删除文件完成: userId={}, total={}, success={}", userId, fileIds.size(), successCount);
        return successCount;
    }

    @Override
    @Slave
    @Cacheable(value = "files", key = "#fileId", unless = "#result == null")
    public FileInfo getFileById(Long fileId) {
        return fileInfoMapper.selectById(fileId);
    }

    @Override
    @Slave
    @Cacheable(value = "files", key = "'hash:' + #fileHash", unless = "#result == null")
    public FileInfo getFileByHash(String fileHash) {
        return fileInfoMapper.selectByFileHash(fileHash);
    }

    @Override
    @Slave
    public PageResponse<FileInfo> queryFiles(String businessType, String businessId, Long userId, PageRequest pageRequest) {
        Page<FileInfo> page = new Page<>(pageRequest.getPage(), pageRequest.getSize());
        IPage<FileInfo> result = fileInfoMapper.selectPage(page, businessType, businessId, userId, 0);
        
        return new PageResponse<FileInfo>(
            result.getRecords(),
            (int) result.getCurrent(),
            (int) result.getSize(),
            result.getTotal()
        );
    }

    @Override
    @Master
    // @OperateLog(module = "文件管理", operation = "复制文件", type = OperateLog.OperationType.CREATE,
    //            detail = "复制文件: sourceId=#{sourceFileId}")
    public FileUploadResult copyFile(Long sourceFileId, FileUploadRequest targetRequest, Long userId) {
        FileInfo sourceFile = getFileById(sourceFileId);
        if (sourceFile == null) {
            throw new RuntimeException("源文件不存在: " + sourceFileId);
        }
        
        if (!checkFilePermission(sourceFileId, userId, "read")) {
            throw new RuntimeException("无源文件访问权限");
        }
        
        // 创建新文件记录
        FileInfo newFile = createFileInfo(targetRequest, null);
        newFile.setStorageType(sourceFile.getStorageType());
        newFile.setStorageKey(sourceFile.getStorageKey());
        newFile.setStoragePath(sourceFile.getStoragePath());
        newFile.setFileHash(sourceFile.getFileHash());
        newFile.setFileSize(sourceFile.getFileSize());
        newFile.setContentType(sourceFile.getContentType());
        
        fileInfoMapper.insert(newFile);
        
        StorageStrategy strategy = getStorageStrategy(sourceFile.getStorageType());
        String accessUrl = strategy.getAccessUrl(sourceFile.getStorageKey(), 0);
        
        FileUploadResult result = new FileUploadResult();
        result.setFileId(newFile.getId());
        result.setStorageKey(sourceFile.getStorageKey());
        result.setAccessUrl(accessUrl);
        result.setFileHash(sourceFile.getFileHash());
        result.setFileSize(sourceFile.getFileSize());
        result.setStorageType(sourceFile.getStorageType());
        
        log.info("文件复制完成: sourceId={}, newFileId={}", sourceFileId, newFile.getId());
        return result;
    }

    @Override
    public boolean checkFilePermission(Long fileId, Long userId, String permission) {
        FileInfo fileInfo = getFileById(fileId);
        if (fileInfo == null) {
            return false;
        }
        
        // 公开文件允许读取
        if ("read".equals(permission) && Boolean.TRUE.equals(fileInfo.getPublicAccess())) {
            return true;
        }
        
        // 文件所有者拥有所有权限
        if (fileInfo.getUploadUserId() != null && fileInfo.getUploadUserId().equals(userId)) {
            return true;
        }
        
        // TODO: 实现基于角色和权限的复杂访问控制
        // 这里可以集成权限系统，检查用户是否有相应的文件操作权限
        
        return false;
    }

    @Override
    @Master
    public void updateFileAccessStats(Long fileId, Long userId) {
        FileInfo fileInfo = getFileById(fileId);
        if (fileInfo != null) {
            int newDownloadCount = (fileInfo.getDownloadCount() == null ? 0 : fileInfo.getDownloadCount()) + 1;
            fileInfoMapper.updateDownloadStats(fileId, newDownloadCount, LocalDateTime.now());
        }
    }

    @Override
    @Slave
    @FeatureToggle(value = "file_preview", description = "文件预览功能")
    public String generatePreviewUrl(Long fileId, Long userId) {
        if (!checkFilePermission(fileId, userId, "read")) {
            throw new RuntimeException("无文件预览权限");
        }
        
        FileInfo fileInfo = getFileById(fileId);
        if (fileInfo == null) {
            throw new RuntimeException("文件不存在");
        }
        
        // TODO: 根据文件类型生成预览URL
        // 支持图片、PDF、Office文档等格式的在线预览
        
        return "/api/files/preview/" + fileId;
    }

    @Override
    @Slave
    public FileStatistics getFileStatistics(String businessType, Long userId) {
        FileStatistics stats = new FileStatistics();
        stats.setTotalFiles(fileInfoMapper.countFiles(businessType, userId, 0));
        stats.setTotalSize(fileInfoMapper.sumFileSize(businessType, userId, 0));
        stats.setTotalDownloads(fileInfoMapper.sumDownloadCount(businessType, userId, 0));
        return stats;
    }
    
    // ==================== 私有方法 ====================
    
    private void validateUploadRequest(FileUploadRequest request) {
        if (request.getFileSize() > maxFileSize) {
            throw new RuntimeException("文件大小超过限制: " + maxFileSize);
        }
    }
    
    private StorageStrategy getStorageStrategy(String storageType) {
        return storageStrategies.stream()
            .filter(strategy -> strategy.getStorageType().equals(storageType))
            .findFirst()
            .orElseThrow(() -> new RuntimeException("不支持的存储类型: " + storageType));
    }
    
    private FileInfo createFileInfo(FileUploadRequest request, FileUploadResult uploadResult) {
        FileInfo fileInfo = new FileInfo();
        
        // 基本信息
        fileInfo.setOriginalFilename(request.getOriginalFilename());
        fileInfo.setFileSize(request.getFileSize());
        fileInfo.setFileHash(request.getFileHash());
        fileInfo.setContentType(request.getContentType());
        fileInfo.setBusinessType(request.getBusinessType());
        fileInfo.setBusinessId(request.getBusinessId());
        fileInfo.setUploadUserId(request.getUploadUserId());
        fileInfo.setPublicAccess(request.getPublicAccess());
        fileInfo.setAccessPermission(request.getAccessPermission());
        
        // 存储信息
        if (uploadResult != null) {
            fileInfo.setStorageType(uploadResult.getStorageType());
            fileInfo.setStorageKey(uploadResult.getStorageKey());
            fileInfo.setStoragePath(request.getStoragePath());
        }
        
        // 文件扩展名
        if (request.getOriginalFilename() != null) {
            String extension = getFileExtension(request.getOriginalFilename());
            fileInfo.setFileExtension(extension);
        }
        
        return fileInfo;
    }
    
    private String getFileExtension(String filename) {
        if (filename == null || filename.isEmpty()) {
            return null;
        }
        int dotIndex = filename.lastIndexOf('.');
        if (dotIndex > 0 && dotIndex < filename.length() - 1) {
            return filename.substring(dotIndex + 1).toLowerCase();
        }
        return null;
    }
}
